package com.cskaoyan.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * @author stone
 * @date 2022/12/21 11:27
 */
@Component
@Aspect
public class CustomAspect {


    /**
     * <aop:pointcut id="pointcut1" expression="execution(* com..service..*(..))"/>
     * <aop:pointcut id="pointcut2" expression="@annotation(com.cskaoyan.annotation.CountTime)"/>
     * <aop:pointcut id="pointcut3" expression="@target(com.cskaoyan.annotation.CountTimeTarget)"/>
     */
    @Pointcut("execution(* com..service..*(..))")
    public void pointcut1() {
    }

    @Pointcut("@annotation(com.cskaoyan.annotation.CountTime)")
    public void pointcut2() {
    }

    @Pointcut("@target(com.cskaoyan.annotation.CountTimeTarget)")
    public void pointcut3() {
    }


    //需求就是，想要讲pointcut3和这个通知结合起来,pointcut3对应的方法，在执行之前执行这个method1通知
    @Before("pointcut3()") // 使用@Before注解，意味着用的是Before通知,Before通知对应的方法会在委托类方法执行之前去执行
    public void method1(JoinPoint joinPoint) {
        System.out.println("Before通知：委托类方法执行之前输出hello");
        System.out.println("------------------");
        System.out.println("通过JoinPoint获得的一些值");
        Object proxy = joinPoint.getThis();
        Object target = joinPoint.getTarget();
        Signature signature = joinPoint.getSignature();
        String methodName = signature.getName();
        Object[] args = joinPoint.getArgs();

        System.out.println(proxy.getClass());
        System.out.println(target.getClass());
        System.out.println(methodName);
        System.out.println(Arrays.asList(args));

        System.out.println("------------------");
    }

    //@After("pointcut3()")
    @After("@target(com.cskaoyan.annotation.CountTimeTarget)")
    public void method2() {
        System.out.println("After通知");
    }

    // @Around

    /**
     * 对应的方法，返回值为Object，这个返回值就是代理对象执行方法的返回结果
     * 形参需要写一个值 ProceedingJoinPoint，这个接口中提供了一个方法 proceed方法 → 执行委托类方法
     */
    @Around("pointcut3()")
    public Object method3(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("Around通知执行之前");
        long start = System.currentTimeMillis();
        // 使用无参的proceed还是有参的proceed取决于是否要在执行委托类方法的时候修改其传入的参数
        // proceed 可以做try-catch 也可以抛出异常
        // 执行委托类方法 → 不修改参数
        Object proceed = proceedingJoinPoint.proceed();
        // 执行委托类方法 → 使用传入的参数执行委托类方法
        //Object proceed = proceedingJoinPoint.proceed(Object[]);
        long end = System.currentTimeMillis();
        System.out.println("委托类方法执行时间：" + (end - start));
        System.out.println("Around通知执行之后");
        return proceed;
    }

    // @AfterReturning → 可以获得委托类方法return的值
    // 在形参中使用Object类型的值来接收
    // 形参的名称要和@AfterReturning注解中的returning属性值一致
    @AfterReturning(value = "pointcut3()", returning = "result")
    public void method4(Object result) {
        System.out.println("AfterReturning通知的形参中接收到的值：" + result);
    }
    // @AfterThrowing→ 可以获得委托类方法抛出的异常
    //在形参中使用Exception(或Throwable)类型的值来接收
    //形参的名称要和@AfterThrowing注解中的throwing属性值一致
    @AfterThrowing(value = "pointcut3()",throwing = "exception")
    public void method5(Exception exception) {//java.lang.ArithmeticException
        System.out.println("AfterThrowing接收到的异常信息：" + exception.getMessage());/// by zero
    }


    //@Around("pointcut4()")
    public Object superAround(ProceedingJoinPoint joinPoint){

        Object proceed = null;
        try {
            System.out.println("这个位置：Before");
            proceed = joinPoint.proceed();
            System.out.println("这个位置：AfterReturning");// proceed就是委托类方法返回的结果
        } catch (Throwable throwable) {
            System.out.println("这个位置：AfterThrowing");
            throwable.printStackTrace();
        } finally {
            System.out.println("这个位置：After");
        }
        return proceed;
    }
}
